"""
交易统计分析器
统一管理所有交易统计指标的计算
"""
from typing import List, Dict, Any
import numpy as np
from collections import defaultdict, deque


class TradeAnalyzer:
    """交易统计分析器 - 统一计算所有交易统计指标"""
    
    def __init__(self, trades: List[Dict], initial_capital: float, current_capital: float, daily_capital_usage: List[Dict]):
        """
        初始化分析器
        
        Args:
            trades: 交易记录列表
            initial_capital: 初始资金
            current_capital: 当前资金
            daily_capital_usage: 每日资金占用记录 [{date: str, capital_used: float}]
        """
        self.trades = trades
        self.initial_capital = initial_capital
        self.current_capital = current_capital
        self.daily_capital_usage = daily_capital_usage
        
        # 缓存计算结果
        self._stats = None
    
    def get_statistics(self, exclude_forced_close: bool = True) -> Dict[str, Any]:
        """
        获取完整的交易统计信息
        
        Args:
            exclude_forced_close: 是否剔除强制平仓交易
            
        Returns:
            包含所有统计指标的字典
        """
        if self._stats is None:
            self._stats = self._calculate_all_statistics(exclude_forced_close)
        return self._stats
    
    def _pair_buy_sell_trades(self, buy_trades: List[Dict], sell_trades: List[Dict]) -> List[Dict]:
        """
        精确配对买入和卖出交易（按股票和时间顺序，先进先出）
        
        Args:
            buy_trades: 买入交易列表
            sell_trades: 卖出交易列表
            
        Returns:
            配对的卖出交易列表（包含对应的买入信息）
        """
        # 按股票分组的买入队列
        buy_queues = defaultdict(deque)
        
        # 将买入交易按股票分组并排序
        for buy in sorted(buy_trades, key=lambda x: x.get('time', '')):
            stock = buy.get('stock', '')
            # 确保每个买入交易都有必要的字段
            buy.setdefault('commission', 0)
            buy_queues[stock].append(buy)
        
        # 配对的卖出交易
        paired_sell_trades = []
        
        for sell in sorted(sell_trades, key=lambda x: x.get('time', '')):
            stock = sell.get('stock', '')
            sell.setdefault('commission', 0)
            
            if stock not in buy_queues or not buy_queues[stock]:
                # 没有匹配的买入交易，跳过
                continue
                
            # 匹配卖出股数对应的买入交易（先进先出）
            remaining_shares = sell.get('shares', 0)
            matched_buys = []
            
            while remaining_shares > 0 and buy_queues[stock]:
                next_buy = buy_queues[stock][0]
                buy_shares = next_buy.get('shares', 0)
                
                if buy_shares <= remaining_shares:
                    # 整个买入交易被匹配
                    matched_buy = buy_queues[stock].popleft()
                    matched_buys.append(matched_buy)
                    remaining_shares -= buy_shares
                else:
                    # 部分匹配 - 使用比例计算（更清晰）
                    match_ratio = remaining_shares / buy_shares
                    partial_buy = {
                        'time': next_buy.get('time'),
                        'stock': stock,
                        'price': next_buy.get('price', 0),
                        'shares': remaining_shares,
                        'amount': next_buy.get('price', 0) * remaining_shares,
                        'commission': next_buy.get('commission', 0) * match_ratio
                    }
                    matched_buys.append(partial_buy)
                    
                    # 更新队列中的买入交易
                    next_buy['shares'] = buy_shares - remaining_shares
                    next_buy['amount'] = next_buy.get('price', 0) * (buy_shares - remaining_shares)
                    next_buy['commission'] = next_buy.get('commission', 0) * (1 - match_ratio)
                    remaining_shares = 0
            
            if remaining_shares == 0:  # 完全匹配
                sell_with_buys = sell.copy()
                sell_with_buys['matched_buys'] = matched_buys
                paired_sell_trades.append(sell_with_buys)
        
        return paired_sell_trades
    
    def _calculate_time_weighted_avg_capital(self) -> float:
        """
        计算时间加权平均使用资金
        
        公式：时间加权平均使用资金 = ∑(每日资金占用) / 总天数
        
        Returns:
            float: 时间加权平均使用资金
        """
        if not self.daily_capital_usage:
            return 0
        
        # 直接使用每日记录计算（已经按天汇总）
        total_capital_days = sum(day['capital_used'] for day in self.daily_capital_usage)
        total_days = len(self.daily_capital_usage)
        
        return total_capital_days / total_days if total_days > 0 else 0
    
    def _calculate_time_weighted_capital_utilization(self) -> float:
        """
        修正：时间加权资金利用率
        
        区分持仓期间利用率和总体利用率，避免空仓日对平均值的稀释效应
        
        Returns:
            float: 总体资金利用率（百分比），考虑空仓期的影响
        """
        if not self.daily_capital_usage or self.initial_capital <= 0:
            return 0.0
        
        # 分离持仓日和空仓日
        holding_days_capital = []
        total_days = len(self.daily_capital_usage)
        holding_days = 0
        
        for day in self.daily_capital_usage:
            if day['capital_used'] > 0:
                holding_days_capital.append(day['capital_used'])
                holding_days += 1
        
        if holding_days == 0:
            return 0.0
        
        # 持仓期间的平均资金占用和利用率
        avg_holding_capital = sum(holding_days_capital) / holding_days
        holding_period_utilization = (avg_holding_capital / self.initial_capital * 100)
        
        # 总体资金利用率（考虑空仓期）
        overall_utilization = holding_period_utilization * (holding_days / total_days)
        
        # 保存持仓期间利用率用于其他计算
        self._holding_period_utilization = holding_period_utilization
        self._avg_holding_capital = avg_holding_capital
        self._overall_utilization = overall_utilization
        self._holding_days = holding_days
        self._total_days = total_days
        
        # 返回总体资金利用率（考虑空仓期，符合"时间加权"的定义）
        return round(overall_utilization, 2)
    
    def _calculate_peak_capital_utilization(self) -> float:
        """
        计算峰值资金利用率（风险控制视角）
        
        公式：峰值资金利用率 = max(每日资金占用) / 初始资金 × 100%
        
        说明：反映策略在回测期间的最大资金占用情况，用于风险控制评估
        
        Returns:
            float: 峰值资金利用率（百分比）
        """
        if not self.daily_capital_usage or self.initial_capital <= 0:
            return 0.0
        
        max_capital_used = max(day['capital_used'] for day in self.daily_capital_usage)
        return (max_capital_used / self.initial_capital * 100) if max_capital_used > 0 else 0.0
    
    def _calculate_capital_usage_return(self, trade_net_profit: float) -> float:
        """
        修正：资金使用收益率
        
        使用持仓期间的平均资金占用作为分母，而不是包含空仓日的平均值
        
        Args:
            trade_net_profit: 交易净盈利
        
        Returns:
            float: 资金使用收益率（百分比）
        """
        # 如果还没有计算持仓期间利用率，先计算
        if not hasattr(self, '_avg_holding_capital') or self._avg_holding_capital == 0:
            # 重新计算持仓期间平均资金占用
            holding_days_capital = []
            for day in self.daily_capital_usage:
                if day['capital_used'] > 0:
                    holding_days_capital.append(day['capital_used'])
            
            if not holding_days_capital:
                return 0.0
            
            self._avg_holding_capital = sum(holding_days_capital) / len(holding_days_capital)
        
        # 资金使用收益率 = 交易净盈利 / 持仓期间平均资金占用
        if self._avg_holding_capital > 0:
            capital_usage_return = (trade_net_profit / self._avg_holding_capital * 100)
        else:
            capital_usage_return = 0.0
        
        return round(capital_usage_return, 2)
    
    def _calculate_all_statistics(self, exclude_forced_close: bool) -> Dict[str, Any]:
        """计算所有统计指标"""
        # 1. 筛选交易
        buy_trades = [t for t in self.trades if t.get('direction') == 'buy' or t.get('action') == 'buy']
        sell_trades = [t for t in self.trades if t.get('direction') == 'sell' or t.get('action') == 'sell']
        
        # 2. 剔除强制平仓（如果需要）
        if exclude_forced_close:
            forced_close_trades = [t for t in sell_trades if t.get('is_forced_close', False)]
            normal_sell_trades = [t for t in sell_trades if not t.get('is_forced_close', False)]
        else:
            forced_close_trades = []
            normal_sell_trades = sell_trades
        
        # 3. 精确配对交易（按股票和时间顺序，先进先出）
        paired_sell_trades = self._pair_buy_sell_trades(buy_trades, normal_sell_trades)
        
        # 4. 计算关键资金指标
        forced_close_profit = sum(t.get('profit', 0) for t in forced_close_trades)
        
        # 统一净盈利计算：使用最终资金 - 初始资金
        net_profit = self.current_capital - self.initial_capital
        
        # 如果排除强制平仓，调整净盈利和最终资金
        if exclude_forced_close:
            adjusted_net_profit = net_profit - forced_close_profit
            final_capital = self.current_capital - forced_close_profit
        else:
            adjusted_net_profit = net_profit
            final_capital = self.current_capital
        
        # 5. 计算时间加权平均使用资金
        avg_capital_used = self._calculate_time_weighted_avg_capital()
        
        # 5.1 计算时间加权资金利用率
        time_weighted_utilization = self._calculate_time_weighted_capital_utilization()
        
        # 5.2 计算峰值资金利用率（风险控制视角）
        peak_utilization = self._calculate_peak_capital_utilization()
        
        # 6. 计算交易相关的盈利（仅来自正常卖出交易，使用已计算好的profit字段）
        # 注意：分时回测已经计算好了精确的profit（基于加权平均成本），直接使用
        trade_profits = []
        trade_losses = []
        all_profits = []
        all_profit_rates = []
        
        for sell_trade in paired_sell_trades:
            # 直接使用交易记录中已计算好的盈亏（分时回测已精确计算）
            profit = sell_trade.get('profit', 0)
            profit_rate = sell_trade.get('profit_rate', 0)
            
            all_profits.append(profit)
            all_profit_rates.append(profit_rate)
            
            if profit > 0:
                trade_profits.append(profit)
            elif profit < 0:
                trade_losses.append(abs(profit))
        
        # 7. 基于交易计算的净盈利（用于资金使用收益率）
        trade_net_profit = sum(all_profits) if all_profits else 0
        
        # 8. 计算收益率指标
        total_asset_return = (adjusted_net_profit / self.initial_capital * 100) if self.initial_capital > 0 else 0
        # 使用时间加权资金利用率
        capital_utilization = time_weighted_utilization
        
        # 计算持仓天数比例
        holding_days_ratio = (getattr(self, '_holding_days', 0) / getattr(self, '_total_days', 1) * 100) if hasattr(self, '_total_days') and self._total_days > 0 else 0.0
        
        # 关键修正：资金使用收益率使用持仓期间的平均资金占用作为分母
        capital_usage_return = self._calculate_capital_usage_return(trade_net_profit)
        
        # 9. 交易统计
        total_trades = len(paired_sell_trades)
        win_trades = len(trade_profits)
        loss_trades = len(trade_losses)
        win_rate = (win_trades / total_trades * 100) if total_trades > 0 else 0
        
        avg_profit = np.mean(trade_profits) if trade_profits else 0
        avg_loss = np.mean(trade_losses) if trade_losses else 0
        profit_loss_ratio = (avg_profit / avg_loss) if avg_loss > 0 else 0
        
        total_profit_amount = sum(trade_profits) if trade_profits else 0
        total_loss_amount = sum(trade_losses) if trade_losses else 0
        
        # 10. 持有期分析
        holding_periods = [t.get('hold_days', 0) for t in paired_sell_trades if t.get('hold_days') is not None]
        avg_holding_period = np.mean(holding_periods) if holding_periods else 0
        median_holding_period = np.median(holding_periods) if holding_periods else 0
        
        # 11. 价格统计
        avg_buy_price = np.mean([t['price'] for t in buy_trades]) if buy_trades else 0
        avg_sell_price = np.mean([t['price'] for t in paired_sell_trades]) if paired_sell_trades else 0
        
        # 12. 收益率分析
        avg_profit_rate = np.mean(all_profit_rates) if all_profit_rates else 0
        max_single_profit = max(trade_profits) if trade_profits else 0
        max_single_loss = max(trade_losses) if trade_losses else 0
        
        # 13. 最大连续亏损
        max_consecutive_losses = 0
        current_consecutive = 0
        for profit in all_profits:
            if profit < 0:
                current_consecutive += 1
                max_consecutive_losses = max(max_consecutive_losses, current_consecutive)
            else:
                current_consecutive = 0
        
        # 返回完整统计
        return {
            # 资金相关
            'initial_capital': self.initial_capital,
            'current_capital': self.current_capital,
            'final_capital': final_capital,
            'net_profit': adjusted_net_profit,  # 调整后的总净盈利
            'trade_net_profit': trade_net_profit,  # 仅来自正常交易的净盈利
            'forced_close_profit': forced_close_profit,
            'has_forced_close': len(forced_close_trades) > 0,
            
            # 资金使用统计
            'avg_capital_used': avg_capital_used,  # 时间加权平均使用资金（包含空仓日）
            'capital_utilization': capital_utilization,  # 时间加权资金利用率（持仓期间）
            'time_weighted_utilization': time_weighted_utilization,  # 时间加权资金利用率（总体，考虑空仓期）
            'overall_utilization': getattr(self, '_overall_utilization', 0),  # 总体资金利用率（考虑空仓期）
            'holding_period_utilization': getattr(self, '_holding_period_utilization', 0),  # 持仓期间资金利用率
            'holding_days_ratio': round(holding_days_ratio, 2),  # 持仓天数比例
            'peak_utilization': peak_utilization,  # 峰值资金利用率（风险控制视角）
            
            # 收益率指标
            'total_asset_return': total_asset_return,  # 总资产收益率（基于初始资金）
            'capital_usage_return': capital_usage_return,  # 资金使用收益率（使用交易净盈利）
            'total_return': total_asset_return,  # 保留兼容性
            
            # 交易次数
            'total_trades': total_trades,
            'buy_count': len(buy_trades),
            'sell_count': len(paired_sell_trades),
            'forced_close_count': len(forced_close_trades),
            
            # 盈亏统计
            'win_trades': win_trades,
            'loss_trades': loss_trades,
            'win_rate': win_rate,
            'profit_loss_ratio': profit_loss_ratio,
            
            # 金额统计
            'total_profit_amount': total_profit_amount,
            'total_loss_amount': total_loss_amount,
            'avg_profit': avg_profit,
            'avg_loss': avg_loss,
            'max_single_profit': max_single_profit,
            'max_single_loss': max_single_loss,
            
            # 持有期
            'holding_periods': holding_periods,
            'avg_holding_period': avg_holding_period,
            'median_holding_period': median_holding_period,
            
            # 价格
            'avg_buy_price': avg_buy_price,
            'avg_sell_price': avg_sell_price,
            
            # 收益率
            'profit_rates': all_profit_rates,
            'avg_profit_rate': avg_profit_rate,
            
            # 风险指标
            'max_consecutive_losses': max_consecutive_losses,
            
            # 详细列表
            'profits': trade_profits,
            'losses': trade_losses,
            'normal_buy_trades': buy_trades,
            'normal_sell_trades': paired_sell_trades,
            'forced_close_trades': forced_close_trades,
        }
    
    def print_summary(self, output_func=print, positions=None, save_to_file=False, log_file=None, validate=True):
        """
        打印交易统计摘要（完全匹配分时/日线回测的输出格式）
        
        Args:
            output_func: 输出函数，默认为print
            positions: 当前持仓字典 {stock: shares}
            save_to_file: 是否保存到文件
            log_file: 日志文件对象
            validate: 是否在打印前验证数据合理性，默认True
        """
        stats = self.get_statistics(exclude_forced_close=True)
        
        # 可选的数据验证
        if validate and not self.validate_statistics(stats):
            output_func("⚠️ 数据验证失败，统计结果可能不准确，请检查计算逻辑")
            output_func("="*80)
            return
        
        # 准备输出函数
        def output(msg=""):
            output_func(msg)
            if save_to_file and log_file:
                try:
                    log_file.write(str(msg) + '\n')
                except:
                    pass
        
        # 基础信息（横向显示）
        profit_amount = stats['final_capital'] - stats['initial_capital']
        return_note = " (已剔除强制平仓)" if stats['has_forced_close'] else ""
        
        output(f"💰 资金: 初始:{stats['initial_capital']:,.0f}元 → 最终:{stats['final_capital']:,.0f}元 → 盈亏:{profit_amount:+,.0f}元 | "
               f"平均使用:{stats['avg_capital_used']:,.0f}元 | 持仓期间利用率:{stats['holding_period_utilization']:.1f}% | "
               f"时间加权利用率:{stats['time_weighted_utilization']:.1f}% | 峰值利用率:{stats['peak_utilization']:.1f}%")
        output(f"📊 资金使用详情: 持仓天数比例:{stats['holding_days_ratio']:.1f}%")
        output(f"📈 收益率: 总资产收益率:{stats['total_asset_return']:+.2f}% | "
               f"资金使用收益率:{stats['capital_usage_return']:+.2f}%{return_note}")
        output(f"📋 交易统计: 总{len(self.trades)}次 | 买入:{stats['buy_count']}次 | 卖出:{stats['sell_count']}次 | "
               f"胜率:{stats['win_rate']:.1f}% | 均买价:{stats['avg_buy_price']:.2f}元 | 均卖价:{stats['avg_sell_price']:.2f}元")
        
        # 使用统一的交易记录显示方法（使用统计计算中的精确数据）
        self._print_trade_records_unified(output, stats)
        
        # 持仓信息
        if positions:
            output("\n📈 当前持仓:")
            for stock, shares in positions.items():
                if shares > 0:
                    output(f"  {stock}: {shares}股")
        else:
            output("\n📈 当前持仓: 无")
        
        output("="*80)
    
    def _print_trade_records_unified(self, output, stats):
        """统一的交易记录显示（使用统计计算中的精确数据）"""
        output("\n📊 交易记录 (按股票汇总):")
        output("="*80)
        
        # 使用统计计算中的配对交易数据
        paired_sell_trades = stats['normal_sell_trades']
        forced_close_trades = stats['forced_close_trades']
        
        # 按股票分组
        trades_by_stock = defaultdict(list)
        for trade in self.trades:
            trades_by_stock[trade.get('stock', '')].append(trade)
        
        for stock in sorted(trades_by_stock.keys()):
            stock_trades = trades_by_stock[stock]
            stock_name = self._get_stock_name(stock)
            
            output()
            stock_display = f"{stock} ({stock_name})" if stock_name else stock
            output(f"【{stock_display}】")
            
            # 直接显示所有交易，使用统计计算中的精确盈亏数据
            for trade in stock_trades:
                time_str = trade.get('time', '')
                direction = trade.get('direction', trade.get('action', ''))
                price = trade.get('price', 0)
                shares = trade.get('shares', 0)
                amount = trade.get('amount', 0)
                reason = trade.get('reason', '')
                
                if direction == 'buy':
                    output(f"  {time_str[:16]} 🔴 {price:.2f}元 × {shares}股 = {amount:,.2f}元 | {reason}")
                    
                elif direction == 'sell':
                    is_forced = trade.get('is_forced_close', False)
                    direction_icon = '⚠️' if is_forced else '🟢'
                    
                    # 查找统计计算中的精确盈亏数据
                    profit = 0
                    profit_rate = 0
                    
                    # 在配对交易中查找
                    for paired_trade in paired_sell_trades:
                        if (paired_trade.get('time') == trade.get('time') and 
                            paired_trade.get('stock') == stock):
                            profit = paired_trade.get('profit', 0)
                            profit_rate = paired_trade.get('profit_rate', 0)
                            break
                    
                    # 如果在正常交易中没找到，检查强制平仓
                    if profit == 0 and is_forced:
                        for forced_trade in forced_close_trades:
                            if (forced_trade.get('time') == trade.get('time') and 
                                forced_trade.get('stock') == stock):
                                profit = forced_trade.get('profit', 0)
                                profit_rate = forced_trade.get('profit_rate', 0)
                                break
                    
                    forced_tag = " [剔除]" if is_forced else ""
                    profit_sign = "+" if profit >= 0 else ""
                    output(f"  {time_str[:16]} {direction_icon} {price:.2f}元 × {shares}股 = {amount:,.2f}元 | "
                           f"盈亏{profit_sign}{profit:,.2f}元({profit_sign}{profit_rate:.1f}%){forced_tag} | {reason}")
    
    def validate_statistics(self, stats: Dict[str, Any]) -> bool:
        """
        验证统计数据的合理性
        
        Args:
            stats: 统计数据字典
            
        Returns:
            bool: 数据是否合理
        """
        # 检查关键指标是否在合理范围内
        expected_net_profit = stats['final_capital'] - stats['initial_capital']
        if abs(stats['net_profit'] - expected_net_profit) > 1.0:
            print(f"⚠️ 净盈利计算不一致: {stats['net_profit']:.2f} vs {expected_net_profit:.2f}")
            return False
        
        if stats['win_rate'] < 0 or stats['win_rate'] > 100:
            print(f"⚠️ 胜率异常: {stats['win_rate']:.2f}%")
            return False
        
        if stats['capital_usage_return'] > 10000:  # 超过10000%可能计算有误
            print(f"⚠️ 资金使用收益率异常高: {stats['capital_usage_return']:.2f}%")
            return False
        
        if stats['total_asset_return'] < -100 or stats['total_asset_return'] > 10000:
            print(f"⚠️ 总资产收益率异常: {stats['total_asset_return']:.2f}%")
            return False
        
        return True
    
    def _get_stock_name(self, stock: str) -> str:
        """获取股票名称"""
        try:
            from xtquant import xtdata
            detail = xtdata.get_instrument_detail(stock)
            if isinstance(detail, dict):
                return detail.get('InstrumentName', '')
        except:
            pass
        return ""

